package com.gitee.neuray.security.generator.generatormd.biz;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.gitee.neuray.security.common.exception.BaseException;
import com.gitee.neuray.security.generator.constants.CodeConstants;
import com.gitee.neuray.security.generator.databasemd.biz.TDatabaseInfoBiz;
import com.gitee.neuray.security.generator.databasemd.entity.TDatabaseInfoEntity;
import com.gitee.neuray.security.generator.enums.EntityColumnsEnum;
import com.gitee.neuray.security.generator.enums.EntityTableEnum;
import com.gitee.neuray.security.generator.enums.GenerateBizParmsEnum;
import com.gitee.neuray.security.generator.enums.GenerationTypeEnum;
import com.gitee.neuray.security.generator.factory.DataSourceFactory;
import com.gitee.neuray.security.generator.generatormd.dto.MultiTableDto;
import com.gitee.neuray.security.generator.generatormd.dto.SingleTableDto;
import com.gitee.neuray.security.generator.strategy.*;
import com.gitee.neuray.security.generator.utils.DataSourceUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipOutputStream;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import com.gitee.neuray.security.common.biz.BaseBiz;

import com.gitee.neuray.security.generator.generatormd.mapper.TGenerateInfoMapper;
import com.gitee.neuray.security.generator.generatormd.entity.TGenerateInfoEntity;

import javax.sql.DataSource;


@Service
@Transactional(rollbackFor = Exception.class)
public class TGenerateInfoBiz extends BaseBiz<TGenerateInfoMapper, TGenerateInfoEntity> {
    @Override
    protected String getPageName() {
        return "TGenerateInfo" + "Biz";
    }

    @Autowired
    private TDatabaseInfoBiz tDatabaseInfoBiz;
    @Autowired
    private DataSourceUtils dataSourceUtils;

    /**
     * 根据sql，将sql转换为表信息
     *
     * @param params
     * @return
     * @throws Exception
     */
    public synchronized List<Map> getTableViewInfo(Map<String, String> params) throws Exception {
        String id = params.get(GenerateBizParmsEnum.dataBase.name());
        TDatabaseInfoEntity tDatabaseInfoEntity = tDatabaseInfoBiz.selectById(id);
        DataSource db = getdb(id, tDatabaseInfoEntity);
        try {
            String sql = params.get(GenerateBizParmsEnum.sql.name());
            String view = dataSourceUtils.createView(db, sql, CodeConstants.CODE_VIEW);
            DataSourceFactory instance = DataSourceFactory.getInstance();
            DataSourceStrategy dataSourceStrategy = instance.getDataSourceStrategy(tDatabaseInfoEntity.getDbType());
            StringBuilder queryColumnsSQL = dataSourceStrategy.queryColumnsSQL(view);
            List<Map> result = null;
            ResultSet resultSet = dataSourceUtils.excuteQuery(db, queryColumnsSQL.toString());
            result = dataSourceUtils.forEachResultSet(resultSet, EntityColumnsEnum.class);
            return result;
        } finally {
            dataSourceUtils.dropView(db, CodeConstants.CODE_VIEW)
                    .close(db, null, null);
        }
    }

    /**
     * 获取指定数据源的所有表信息
     *
     * @param id
     * @return
     * @throws Exception
     */
    public List<Map> getTables(String id) throws Exception {
        TDatabaseInfoEntity tDatabaseInfoEntity = tDatabaseInfoBiz.selectById(id);
        DataSource db = getdb(id, tDatabaseInfoEntity);
        try {
            DataSourceStrategy dataSourceStrategy = getDataSourceStrategy(tDatabaseInfoEntity.getDbType());
            StringBuilder queryTableSQL = dataSourceStrategy.queryTablesSQL();
            ResultSet resultSet = dataSourceUtils.excuteQuery(db, queryTableSQL.toString());
            List<Map> result = dataSourceUtils.forEachResultSet(resultSet, EntityTableEnum.class);
            return result;
        } finally {
            dataSourceUtils
                    .close(db, null, null);
        }
    }

    /**
     * 指定数据域，获取指定表的详细信息
     *
     * @param id
     * @param tableName
     * @return
     * @throws Exception
     */
    public List<Map> getTableInfo(String id, String tableName) throws Exception {
        TDatabaseInfoEntity tDatabaseInfoEntity = tDatabaseInfoBiz.selectById(id);
        DataSource db = getdb(id, tDatabaseInfoEntity);
        try {
            DataSourceStrategy dataSourceStrategy = getDataSourceStrategy(tDatabaseInfoEntity.getDbType());
            StringBuilder queryColumnsSQL = dataSourceStrategy.queryColumnsSQL(tableName);
            ResultSet resultSet = dataSourceUtils.excuteQuery(db, queryColumnsSQL.toString());
            List<Map> result = dataSourceUtils.forEachResultSet(resultSet, EntityColumnsEnum.class);
            return result;
        } finally {
            dataSourceUtils
                    .close(db, null, null);
        }
    }

    /**
     * 获取指定数据源
     *
     * @param id
     * @param tDatabaseInfoEntity
     * @return
     */
    private DataSource getdb(String id, TDatabaseInfoEntity tDatabaseInfoEntity) {
        checkId(id);
        checkEntity(tDatabaseInfoEntity);
        return dataSourceUtils.createdb(tDatabaseInfoEntity);
    }

    /**
     * 获取策略元
     *
     * @param type
     * @return
     */
    private DataSourceStrategy getDataSourceStrategy(String type) {
        if (StringUtils.isEmpty(type)) {
            throw new BaseException("数据库类型不能为空...");
        }
        DataSourceFactory instance = DataSourceFactory.getInstance();
        DataSourceStrategy dataSourceStrategy = instance.getDataSourceStrategy(type);
        return dataSourceStrategy;
    }

    /**
     * 单表生成代码
     *
     * @param singleTableDto
     * @return
     * @throws Exception
     */
    public byte[] singleGenerator(SingleTableDto singleTableDto) throws Exception {
        //1.保存生成数据
        //2.渲染代码模板
        TDatabaseInfoEntity tDatabaseInfoEntity = tDatabaseInfoBiz.selectById(singleTableDto.getDataBase());
        DataSource db = getdb(singleTableDto.getDataBase(), tDatabaseInfoEntity);
        DataSourceStrategy dataSourceStrategy = getDataSourceStrategy(tDatabaseInfoEntity.getDbType());
        StringBuilder queryTableSQL = dataSourceStrategy.queryTableSQL(singleTableDto.getTableName());
        try {
            ResultSet table = dataSourceUtils.excuteQuery(db, queryTableSQL.toString());
            Map<String, String> tableInfo = dataSourceUtils.forEachResultSetMap(table, EntityTableEnum.class);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ZipOutputStream zip = new ZipOutputStream(outputStream);
            //生成代码
            GenerateStrategy generateStrategy = new GenSingleTableStrategy();
            List<Map> columnEntitys = JSONArray.parseArray(JSONObject.toJSONString(singleTableDto.getTable()),Map.class);
            GenBaseBizEntityStrategy bizParams =JSONObject.parseObject(JSONObject.toJSONString(singleTableDto),GenBaseBizEntityStrategy.class);
            generateStrategy.start(bizParams,tableInfo, columnEntitys, zip);
            IOUtils.closeQuietly(zip);
            return outputStream.toByteArray();
        } finally {
            dataSourceUtils.close(db, null, null);
        }
        //3.上传到文件服务器
    }

    /**
     * 多表生成
     * @param multiTableDto
     * @return
     * @throws Exception
     */
    public byte[] multGenerator(MultiTableDto multiTableDto) throws Exception {
        //1.保存生成数据
        //2.渲染代码模板
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        //生成代码
        GenerateStrategy generateStrategy = new GenMultiTableStrategy();
        GenBaseBizEntityStrategy bizParams =JSONObject.parseObject(JSONObject.toJSONString(multiTableDto),GenBaseBizEntityStrategy.class);
        Map<String,String> tableInfo = new HashMap<>(256);
        //tableName使用module，tableComment 使用主题
        tableInfo.put("tableName",multiTableDto.getModuleName());
        tableInfo.put("tableComment",multiTableDto.getGName());
        tableInfo.put(GenerateBizParmsEnum.sql.name(),multiTableDto.getSql());
        List<Map> columnEntitys = JSONArray.parseArray(JSONObject.toJSONString(multiTableDto.getTable()),Map.class);
        generateStrategy.start(bizParams,tableInfo, columnEntitys, zip);
        IOUtils.closeQuietly(zip);
        return outputStream.toByteArray();
        //3.上传到文件服务器
    }

    /**
     * 校验id
     *
     * @param id
     * @throws BaseException
     */
    private void checkId(String id) throws BaseException {
        if (StringUtils.isEmpty(id)) {
            throw new BaseException("选择的数据库id不能为空或null...");
        }
    }

    /**
     * 校验库中是否存在数据
     *
     * @param entity
     */
    private void checkEntity(Object entity) {
        if (entity == null) {
            throw new BaseException("查询不到数据库记录，传入的参数可能发生错误...");
        }
    }
}
